# 学习处理文件和保存数据可让你的程序使用起来更容易：用户将能够选择输入什么样的数据，以及在什么时候输入；用户使用你的程序做一些工作后，可将程序关闭，以后再接着往下做。学习处理异常可帮助你应对文件不存在的情形，以及处理其他可能导致程序崩溃的问题。这让你的程序在面对错误的数据时更健壮——不管这些错误数据源自无意的错误，还是源自破坏程序的恶意企图。你在本章学习的技能可提高程序的适用性、可用性和稳定性。

# 从文件中读取数据文本文件可存储的数据量多得难以置信：天气数据、交通数据、社会经济数据、文学作品等。每当需要分析或修改存储在文件中的信息时，读取文件都很有用，对数据分析应用程序来说尤其如此。例如，你可以编写一个这样的程序：读取一个文本文件的内容，重新设置这些数据的格式并将其写入文件，让浏览器能够显示这些内容。
# 读取整个文件要读取文件，需要一个包含几行文本的文件。下面首先来创建一个文件，它包含精确到小数点后30位的圆周率值，且在小数点后每10位处都换行：
with open('D:\pythonstu\python编程从入门到实战第三版读书笔记\pi_digits.txt') as file_object:
    contents = file_object.read()
    print(contents.rstrip())
# 在这个程序中，第1行代码做了大量的工作。我们先来看看函数open()。要以任何方式使用文件——哪怕仅仅是打印其内容，都得先打开文件，这样才能访问它。函数open()接受一个参数：要打开的文件的名称。Python在当前执行的文件所在的目录中查找指定的文件。在这个示例中，当前运行的是file_reader.py，因此Python在file_reader.py所在的目录中查找pi_digits.txt。函数open()返回一个表示文件的对象。在这里，open('pi_digits.txt')返回一个表示文件pi_digits.txt的对象；Python将这个对象存储在我们将在后面使用的变量中。
# 关键字with在不再需要访问文件后将其关闭。在这个程序中，注意到我们调用了open()，但没有调用close()；你也可以调用open()和close()来打开和关闭文件，但这样做时，如果程序存在bug，导致close()语句未执行，文件将不会关闭。这看似微不足道，但未妥善地关闭文件可能会导致数据丢失或受损。如果在程序中过早地调用close()，你会发现需要使用文件时它已关闭（无法访问），这会导致更多的错误。并非在任何情况下都能轻松确定关闭文件的恰当时机，但通过使用前面所示的结构，可让Python去确定：你只管打开文件，并在需要时使用它，Python自会在合适的时候自动将其关闭。
# 有了表示pi_digits.txt的文件对象后，我们使用方法read()（前述程序的第2行）读取这个文件的全部内容，并将其作为一个长长的字符串存储在变量contents中。这样，通过打印contents的值，就可将这个文本文件的全部内容显示出来

# 为何会多出这个空行呢？因为read()到达文件末尾时返回一个空字符串，而将这个空字符串显示出来时就是一个空行。要删除末尾的空行，可在print语句中使用rstrip()：
# 逐行读取读取文件时，常常需要检查其中的每一行：你可能要在文件中查找特定的信息，或者要以某种方式修改文件中的文本。例如，你可能要遍历一个包含天气数据的文件，并使用天气描述中包含字样sunny的行。在新闻报道中，你可能会查找包含标签<headline>的行，并按特定的格式设置它。要以每次一行的方式检查文件，可对文件对象使用for循环：
for line in file_object:
    print(line.rstrip())

# 创建一个包含文件各行内容的列表使用关键字with时，open()返回的文件对象只在with代码块内可用。如果要在with代码块外访问文件的内容，可在with代码块内将文件的各行存储在一个列表中，并在with代码块外使用该列表：你可以立即处理文件的各个部分，也可推迟到程序后面再处理。下面的示例在with代码块中将文件pi_digits.txt的各行存储在一个列表中，再在with代码块外打印它们：
lines = file_object.readlines()
for line in lines:
    print(line.rstrip())
    
# 使用文件的内容将文件读取到内存中后，就可以以任何方式使用这些数据了。下面以简单的方式使用圆周率的值。首先，我们将创建一个字符串，它包含文件中存储的所有数字，且没有任何空格：
filename = 'D:\pythonstu\python编程从入门到实战第三版读书笔记\pi_digits.txt'
with open(filename) as file_object:
    lines = file_object.readlines()
pi_string = ''
for line in lines:
    pi_string+= line.strip()
print(pi_string) 
print(len(pi_string))
# 在变量pi_string存储的字符串中，包含原来位于每行左边的空格，为删除这些空格，可使用strip()而不是rstrip()：
# 写入空文件要将文本写入文件，你在调用open()时需要提供另一个实参，告诉Python你要写入打开的文件。为明白其中的工作原理，我们来将一条简单的消息存储到文件中，而不是将其打印到屏幕上：
with open(filename,'w') as file_object:
    file_object.write("I love programming.") 

# 在这个示例中，调用open()时提供了两个实参（见❶）。第一个实参也是要打开的文件的名称；第二个实参('w')告诉Python，我们要以写入模式打开这个文件。打开文件时，可指定读取模式('r')、写入模式('w')、附加模式('a')或让你能够读取和写入文件的模式('r+')。如果你省略了模式实参，Python将以默认的只读模式打开文件
# 注意　Python只能将字符串写入文本文件。要将数值数据存储到文本文件中，必须先使用函数str()将其转换为字符串格式。

# 写入多行函数write()不会在你写入的文本末尾添加换行符，因此如果你写入多行时没有指定换行符，文件看起来可能不是你希望的那样：
filename = 'programming.txt'
with open(filename,'w') as file_object:
    file_object.write("I love programming.\n")
    file_object.write("I love creating new games.\n")
    
# 附加到文件如果你要给文件添加内容，而不是覆盖原有的内容，可以附加模式打开文件。你以附加模式打开文件时，Python不会在返回文件对象前清空文件，而你写入到文件的行都将添加到文件末尾。如果指定的文件不存在，Python将为你创建一个空文件。


# 异常Python使用被称为异常的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时，它都会创建一个异常对象。如果你编写了处理该异常的代码，程序将继续运行；如果你未对异常进行处理，程序将停止，并显示一个traceback，其中包含有关异常的报告。异常是使用try-except代码块处理的。try-except代码块让Python执行指定的操作，同时告诉Python发生异常时怎么办。使用了try-except代码块时，即便出现异常，程序也将继续运行：显示你编写的友好的错误消息，而不是令用户迷惑的traceback。

# 处理ZeroDivisionError异常下面来看一种导致Python引发异常的简单错误。你可能知道不能将一个数字除以0，但我们还是让Python这样做吧：
print(5/0)

# 这会导致ZeroDivisionError异常。Python会创建一个异常对象，并将其存储在变量中。你可以使用try-except代码块来处理这个异常：
# 使用try-except代码块当你认为可能发生了错误时，可编写一个try-except代码块来处理可能引发的异常。你让Python尝试运行一些代码，并告诉它如果这些代码引发了指定的异常，该怎么办。处理ZeroDivisionError异常的try-except代码块类似于下面这样：
try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")
else:
    print("The program completed successfully.")

# 在这个代码中，try代码块尝试运行5/0，这会导致ZeroDivisionError异常。如果异常发生，Python将执行except代码块。在这个例子中，except代码块打印一条友好的消息，而不是显示traceback。else代码块将在没有引发异常时执行，并打印一条消息。

# 处理FileNotFoundError异常使用文件时，一种常见的问题是找不到文件：你要查找的文件可能在其他地方、文件名可能不正确或者这个文件根本就不存在。对于所有这些情形，都可使用try-except代码块以直观的方式进行处理。
filename = 'alice.txt'
try:
    with open(filename) as f_obj:
        contents = f_obj.read()
except FileNotFoundError:
    msg = "Sorry,the file "+filename+" does not exist."
    print(msg)

# 使用json.dump()和json.load()我们来编写一个存储一组数字的简短程序，再编写一个将这些数字读取到内存中的程序。第一个程序将使用json.dump()来存储这组数字，而第二个程序将使用json.load()。函数json.dump()接受两个实参：要存储的数据以及可用于存储数据的文件对象。下面演示了如何使用json.dump()来存储数字列表：
import json
numbers = [2,3,5,7,11,13]
filename = 'numbers.json'
with open(filename,'w') as f_obj:
    json.dump(numbers,f_obj) 
with open(filename) as f_obj:
    data = json.load(f_obj)#这是一种在程序之间共享数据的简单方式
    print(data)
# 我们先导入模块json，再创建一个数字列表。在❶处，我们指定了要将该数字列表存储到其中的文件的名称。通常使用文件扩展名.json来指出文件存储的数据为JSON格式。接下来，我们以写入模式打开这个文件，让json能够将数据写入其中（见❷）。在❸处，我们使用函数json.dump()将数字列表存储到文件numbers.json中。
# 在这个程序中，我们先导入模块json。在❶处，我们创建一个数字列表。在❷处，我们指定了要将该数字列表存储到其中的文件的名称。通常使用文件扩展名.json来指出文件存储的数据为JSON格式。接下来，我们以写入模式打开这个文件，让json能够将数据写入其中（见❸）。在❹处，我们使用函数json.load()将数据从文件中读取到内存中，并打印出来。
# 读取JSON数据到内存中使用json.load()函数读取JSON格式的数据到内存中。json.load()函数接受一个文件对象作为参数，并将JSON格式的数据从文件中读取到内存中。下面演示了如何使用json.load()来读取数字列表：
import json
filename = 'numbers.json'
with open(filename) as f_obj:
    data = json.load(f_obj)
    print(data)

# 在这个程序中，我们先导入模块json。在❶处，我们指定了要从其读取数据的文件的名称。接下来，我们以只读模式打开这个文件，让json能够读取数据（见❷）。在❸处，我们使用函数json.load()将数据从文件中读取到内存中，并打印出来。

# 保存和读取用户生成的数据对于用户生成的数据，使用json保存它们大有裨益，因为如果不以某种方式进行存储，等程序停止运行时用户的信息将丢失。

# 你经常会遇到这样的情况：代码能够正确地运行，但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。遇到具体问题时，你可能需要重构你的代码,这里就不做赘述了。
